using System;
using System.Collections.Generic;
using TTDotNet.Util;
using TTDotNet.Engine.ThreeDEngine;
using TTDotNet.Engine.Stations;
using TTDotNet.Engine.Industries;
using TTDotNet.Engine.Companies;
using TTDotNet.Engine.Files;
using Microsoft.DirectX;

namespace TTDotNet.Engine.Stations
{
    public enum StationType { Bus_Station, Lorry_Station, Road_Depot, Dock, Train, Airport }

    public class Station : Building 
    {
        private static Dictionary<StationType, StationInfo> info;

        protected StationType stationType;
        protected Company companyOwnedBy;
        protected int catchmentArea;

        protected Dictionary<CargoType, Dictionary<Industry, float>> cargoAtStation;

        private List<CargoType> accepts;
        protected Dictionary<CargoType, List<Industry>> industriesInCatchment;
        private List<Coordinate> areaCovered;
        
        static Station()
        {
            StationLoader loader = new StationLoader();
            info = loader.GetStationInfo();
        }

        public Station(string name, Company company, Coordinate coord, RotateType rotType, StationType type)
            : base(new Vector3(coord.X, Game.gameWorld.Map.GetTileMaxHeight(coord.X, coord.Z) / Game.heightScale, coord.Z))
        {

            stationType = type;
            this.companyOwnedBy = company;
            accepts = new List<CargoType>();
            industriesInCatchment = new Dictionary<CargoType, List<Industry>>();
            cargoAtStation = new Dictionary<CargoType, Dictionary<Industry, float>>();

            Name = name;
            
            Random r = new Random();
            int meshNum = r.Next(Station.info[type].MeshesAvailable);

            buildingInfo = info[type].GetBuildingInfo(meshNum);

            bool clear = NotifyTiles();

            if (clear)
            {
                SetupAreaCoverage(coord);
                FindIndustries();
                SetupMesh(coord, rotType, ObjectType.Station);
            }
            else
            {

            }
            
        }

        protected virtual void SetupAreaCoverage(Coordinate coord) { }

        protected virtual void FindIndustries() {}

        public List<CargoType> Accepts
        {
            get { return accepts; }
        }

        public void AddAcceptingCargo(CargoType cargoType)
        {
            bool accepted = false;

            foreach (CargoType ct in accepts)
            {
                if (ct == cargoType) accepted = true;
            }

            if (!accepted) accepts.Add(cargoType);

        }

        public List<CargoPacket> SendOutCargo(CargoType type, int maxAmount)
        {
            if (cargoAtStation.ContainsKey(type))
            {
                return GetCargoFromStorage(type, maxAmount);
            }

            return new List<CargoPacket>();
        }

        public float CargoAvailable(CargoType type)
        {
            float available = 0;

            foreach (Industry ind in cargoAtStation[type].Keys)
            {
                available += cargoAtStation[type][ind];
            }

            return available;
        }

        public List<CargoPacket> GetCargoFromStorage(CargoType type, int maxAmount)
        {
            int currentlyLoaded = 0;
            List<CargoPacket> packets = new List<CargoPacket>();
            List<Industry> industriesEmptied = new List<Industry>();

            foreach (Industry ind in cargoAtStation[type].Keys)
            {
                if (cargoAtStation[type][ind] > (maxAmount - currentlyLoaded))
                {
                    packets.Add(new CargoPacket(new CargoInfo(type, (maxAmount - currentlyLoaded)), Game.gameWorld.CurrentDate, ind));
                    cargoAtStation[type][ind] -= (maxAmount - currentlyLoaded);
                    break;
                }
                else
                {
                    packets.Add(new CargoPacket(new CargoInfo(type, cargoAtStation[type][ind]), Game.gameWorld.CurrentDate, ind));
                    industriesEmptied.Add(ind);
                }
            }

            foreach (Industry ind in industriesEmptied)
            {
                cargoAtStation[type][ind] = 0;
            }

            return packets;
        }

        public void ReceiveCargoFromIndustry(Industry from, CargoInfo packet)
        {
            if (cargoAtStation[packet.Cargo].ContainsKey(from))
            {
                cargoAtStation[packet.Cargo][from] += packet.Amount;
            }
            else
            {
                cargoAtStation[packet.Cargo][from] = packet.Amount;
            }
        }

        public decimal ReceiveCargoFromVehicle(List<CargoPacket> packetList)
        {
            return DeliverToIndustry(packetList);
        }

        private decimal DeliverToIndustry(List<CargoPacket> packetList)
        {
            decimal moneyEarnt = 0;

            try
            {

                int numAccepting = industriesInCatchment[packetList[0].CargoType].Count;
                

                foreach (CargoPacket packet in packetList)
                {
                    foreach (Industry i in industriesInCatchment[packet.CargoType])
                    {
                        moneyEarnt += i.ReceiveCargo(new CargoPacket(new CargoInfo(packet.CargoType, packet.CargoAmount / (float)numAccepting), packet.DatePickedUp, packet.SourceIndustry));
                    }
                }
            }
            catch (Exception e)
            {

            }

            return moneyEarnt;
        }

        public void RemoveAcceptingCargo(CargoType cargoType)
        {
            accepts.Remove(cargoType);
        }

        public float GetAmountWaiting(CargoType type)
        {
            float amount = 0;

            foreach (Industry ind in cargoAtStation[type].Keys)
            {
                amount += cargoAtStation[type][ind];
            }

            return amount;
        }

        public Dictionary<CargoType, Dictionary<Industry, float>> CargoWaiting
        {
            get { return cargoAtStation; }
        }

    }

    public struct StationInfo
    {
        private string stationName;
        private int catchmentDepth;
        private List<BuildingInfo> meshTypes;

        public StationInfo(string name, int depth, List<BuildingInfo> infos)
        {
            stationName = name;
            meshTypes = infos;
            catchmentDepth = depth;
        }

        public BuildingInfo GetBuildingInfo(int num)
        {
            return meshTypes[num];
        }

        public void SetCatchmentDepth(int depth)
        {
            catchmentDepth = depth;
        }

        public int MeshesAvailable
        {
            get
            {
                return meshTypes.Count;
            }
        }

        public string Name
        {
            get
            {
                return stationName;
            }
        }

        public int CatchmentDepth
        {
            get
            {
                return catchmentDepth;
            }
        }
    }
}
